﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

namespace Noris.Tools.FrxEditor.Components
{
	/// <summary>
	/// Třída extenzí pro grafické class
	/// </summary>
	public static class DrawingExtensions
	{
		#region Color: Shift
        /// <summary>
        /// Vrací danou barvu s daným posunutím
        /// </summary>
        /// <param name="root">Výchozí barva</param>
        /// <param name="shift">Posunutí barvy</param>
        /// <returns></returns>
		public static Color Shift(this Color root, float shift)
        {
            float r = (float)root.R + shift;
            float g = (float)root.G + shift;
            float b = (float)root.B + shift;
            return GetColor(root.A, r, g, b);
        }
        /// <summary>
        /// Vrací danou barvu s daným posunutím
        /// </summary>
        /// <param name="root">Výchozí barva</param>
        /// <param name="shiftR">Posunutí barvy pro složku R</param>
        /// <param name="shiftG">Posunutí barvy pro složku G</param>
        /// <param name="shiftB">Posunutí barvy pro složku B</param>
        /// <returns></returns>
        public static Color Shift(this Color root, float shiftR, float shiftG, float shiftB)
        {
            float r = (float)root.R + shiftR;
            float g = (float)root.G + shiftG;
            float b = (float)root.B + shiftB;
            return GetColor(root.A, r, g, b);
        }
        /// <summary>
        /// Vrací danou barvu s daným posunutím
        /// </summary>
        /// <param name="root">Výchozí barva</param>
        /// <param name="shiftA">Posunutí barvy pro složku A</param>
        /// <param name="shiftR">Posunutí barvy pro složku R</param>
        /// <param name="shiftG">Posunutí barvy pro složku G</param>
        /// <param name="shiftB">Posunutí barvy pro složku B</param>
        /// <returns></returns>
        public static Color Shift(this Color root, float shiftA, float shiftR, float shiftG, float shiftB)
        {
            float a = (float)root.A + shiftA;
            float r = (float)root.R + shiftR;
            float g = (float)root.G + shiftG;
            float b = (float)root.B + shiftB;
            return GetColor(a, r, g, b);
        }
        /// <summary>
        /// Vrací barvu dle daných složek, přičemž složky (a,r,g,b) omezuje do rozsahu 0 - 255.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="r"></param>
        /// <param name="g"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        private static Color GetColor(float a, float r, float g, float b)
        {
            int ac = (a < 0f ? 0 : (a > 255f ? 255 : (int)a));
            int rc = (r < 0f ? 0 : (r > 255f ? 255 : (int)r));
            int gc = (g < 0f ? 0 : (g > 255f ? 255 : (int)g));
            int bc = (b < 0f ? 0 : (b > 255f ? 255 : (int)b));
            return Color.FromArgb(ac, rc, gc, bc);
        }
		#endregion
        #region Color: Change
        /// <summary>
        /// Změní barvu.
        /// Změna (Change) není posun (Shift): shift přičítá / odečítá hodnotu, ale změna hodnotu mění koeficientem.
        /// Pokud je hodnota složky například 170 a koeficient změny 0.25, pak výsledná hodnota je +25% od výchozí hodnoty směrem k maximu (255): 170 + 0.25 * (255 - 170).
        /// Obdobně změna dolů -70% z hodnoty 170 dá výsledek 170 - 0.7 * (170).
        /// </summary>
		/// <param name="root">Výchozí barva</param>
		/// <param name="change">Změna složek</param>
		/// <returns></returns>
        public static Color ChangeColor(this Color root, float change)
        {
            float r = ChangeCC(root.R, change);
            float g = ChangeCC(root.G, change);
            float b = ChangeCC(root.B, change);
            return GetColor(root.A, r, g, b);
        }
        /// <summary>
        /// Změní barvu.
        /// Změna (Change) není posun (Shift): shift přičítá / odečítá hodnotu, ale změna hodnotu mění koeficientem.
        /// Pokud je hodnota složky například 170 a koeficient změny 0.25, pak výsledná hodnota je +25% od výchozí hodnoty směrem k maximu (255): 170 + 0.25 * (255 - 170).
        /// Obdobně změna dolů -70% z hodnoty 170 dá výsledek 170 - 0.7 * (170).
        /// </summary>
        /// <param name="root">Výchozí barva</param>
        /// <param name="changeR">Změna složky R</param>
		/// <param name="changeG">Změna složky R</param>
		/// <param name="changeB">Změna složky R</param>
		/// <returns></returns>
        public static Color ChangeColor(this Color root, float changeR, float changeG, float changeB)
        {
            float r = ChangeCC(root.R, changeR);
            float g = ChangeCC(root.G, changeG);
            float b = ChangeCC(root.B, changeB);
            return GetColor(root.A, r, g, b);
        }
        /// <summary>
        /// Vrátí složku změněnou koeficientem.
        /// </summary>
        /// <param name="colorComponent"></param>
        /// <param name="change"></param>
        /// <returns></returns>
        private static float ChangeCC(int colorComponent, float change)
        {
            float result = (float)colorComponent;
            if (change > 0f)
            {
                result = result + (change * (255f - result));
            }
            else if (change < 0f)
            {
                result = result - (-change * result);
            }
            return result;
        }
        #endregion
		#region Color: Morph
		/// <summary>
		/// Vrací barvu, která je výsledkem interpolace mezi barvou this a barvou other, 
		/// přičemž od barvy this se liší poměrem morph.
		/// Poměr (morph): 0=vrací se výchozí barva (this).
		/// Poměr (morph): 1=vrací se barva cílová (other).
		/// Poměr může být i větší než 1 (pak je výsledek ještě za cílovou barvou other),
        /// anebo může být záporný (pak výsledkem je barva na opačné straně než je other).
		/// </summary>
		/// <param name="root">Výchozí barva</param>
		/// <param name="other">Cílová barva</param>
		/// <param name="morph">Poměr morph (0=vrátí this, 1=vrátí other, hodnota může být záporná i větší než 1f)</param>
		/// <returns></returns>
		public static Color Morph(this Color root, Color other, float morph)
		{
			float a = root.A;
			float r = GetMorph(root.R, other.R, morph);
			float g = GetMorph(root.G, other.G, morph);
			float b = GetMorph(root.B, other.B, morph);
			return GetColor(a, r, g, b);
		}
		/// <summary>
		/// Vrátí složku barvy vzniklou morphingem = interpolací.
		/// </summary>
		/// <param name="root">Výchozí složka</param>
		/// <param name="other">Cílová složka</param>
		/// <param name="morph">Poměr morph (0=vrátí this, 1=vrátí other, hodnota může být záporná i větší než 1f)</param>
		/// <returns></returns>
		private static float GetMorph(float root, float other, float morph)
		{
			float dist = other - root;
			return root + morph * dist;
		}
		#endregion
        #region Color: Contrast
        /// <summary>
        /// Vrátí kontrastní barvu černou nebo bílou k barvě this.
        /// </summary>
        /// <param name="root">Výchozí barva</param>
        /// <returns></returns>
        public static Color Contrast(this Color root)
        {
            float a = root.A;
            int q = 184;
            // Určím, kolik složek je světlejších než (q):
            int c = (root.R >= q ? 1 : 0) +
                    (root.G >= q ? 1 : 0) +
                    (root.B >= q ? 1 : 0);
            // Pokud jsou dvě nebo tři složky světlejší než (q), pak vracím černou, jinak bílou:
            return (c >= 2 ? Color.Black : Color.White);
        }

        /// <summary>
        /// Vrátí barvu, která je kontrastní vůči barvě this.
        /// Kontrastní barva leží o dané množství barvy směrem k protilehlé barvě (vždy na opačnou stranu od hodnoty 128), v každé složce zvlášť.
        /// Například ke složce s hodnotou 160 je kontrastní barvou o 32 hodnota (160-32) = 128, k hodnotě 100 o 32 je kontrastní (100+32) = 132.
        /// Tedy kontrastní barva k barvě ((rgb: 64,96,255), contrast=32) je barva: rgb(96,128,223) = (64+32, 96+32, 255-32).
        /// Složka A se nemění.
        /// </summary>
        /// <param name="root">Výchozí barva</param>
        /// <param name="contrast">Míra kontrastu</param>
        /// <returns></returns>
        public static Color Contrast(this Color root, int contrast)
        {
            float a = root.A;
            float r = GetContrast(root.R, contrast);
            float g = GetContrast(root.G, contrast);
            float b = GetContrast(root.B, contrast);
            return GetColor(a, r, g, b);
        }
        /// <summary>
        /// Vrátí barvu, která je kontrastní vůči barvě this.
        /// Kontrastní barva leží o dané množství barvy směrem k protilehlé barvě (vždy na opačnou stranu od hodnoty 128), v každé složce zvlášť.
        /// Například ke složce s hodnotou 160 je kontrastní barvou o 32 hodnota (160-32) = 128, k hodnotě 100 o 32 je kontrastní (100+32) = 132.
        /// Tedy kontrastní barva k barvě ((rgb: 64,96,255), contrast=32) je barva: rgb(96,128,223) = (64+32, 96+32, 255-32).
        /// Složka A se nemění.
        /// </summary>
        /// <param name="root">Výchozí barva</param>
        /// <param name="contrastR">Míra kontrastu ve složce R</param>
        /// <param name="contrastG">Míra kontrastu ve složce G</param>
        /// <param name="contrastB">Míra kontrastu ve složce B</param>
        /// <returns></returns>
        public static Color Contrast(this Color root, int contrastR, int contrastG, int contrastB)
        {
            float a = root.A;
            float r = GetContrast(root.R, contrastR);
            float g = GetContrast(root.G, contrastG);
            float b = GetContrast(root.B, contrastB);
            return GetColor(a, r, g, b);
        }
        /// <summary>
        /// Vrací kontrastní složku
        /// </summary>
        /// <param name="root"></param>
        /// <param name="contrast"></param>
        /// <returns></returns>
        private static float GetContrast(int root, int contrast)
        {
            return (root <= 128 ? root + contrast : root - contrast);
        }
        #endregion
        #region Point, PointF: Relative/Absolute
        /// <summary>
        /// Vrátí relativní bod pro souřadnice bodu this, pokud by počátek byl v daném bodě basePoint.
        /// </summary>
        /// <param name="absolutePoint"></param>
        /// <param name="basePoint"></param>
        /// <returns></returns>
        public static Point GetRelativePointFromBase(this Point absolutePoint, Point basePoint)
        {
            return new Point(absolutePoint.X - basePoint.X, absolutePoint.Y - basePoint.Y);
        }
        /// <summary>
        /// Vrátí absolutní bod pro souřadnice bodu this, pokud by počátek byl v daném bodě basePoint.
        /// </summary>
        /// <param name="point"></param>
        /// <param name="relativePoint"></param>
        /// <returns></returns>
        public static Point GetAbsolutePointFromBase(this Point point, Point relativePoint)
        {
            return new Point(relativePoint.X + point.X, relativePoint.Y + point.Y);
        }
		/// <summary>
		/// Vrátí relativní bod pro souřadnice bodu this, pokud by počátek byl v daném bodě basePoint.
		/// </summary>
		/// <param name="absolutePoint"></param>
		/// <param name="basePoint"></param>
		/// <returns></returns>
		public static PointF GetRelativePointFromBase(this Point absolutePoint, PointF basePoint)
		{
			return new PointF((float)absolutePoint.X - basePoint.X, (float)absolutePoint.Y - basePoint.Y);
		}
		/// <summary>
		/// Vrátí absolutní bod pro souřadnice bodu this, pokud by počátek byl v daném bodě basePoint.
		/// </summary>
		/// <param name="point"></param>
		/// <param name="relativePoint"></param>
		/// <returns></returns>
		public static PointF GetAbsolutePointFromBase(this Point point, PointF relativePoint)
		{
			return new PointF((float)relativePoint.X + point.X, (float)relativePoint.Y + point.Y);
		}
		/// <summary>
		/// Vrátí relativní bod pro souřadnice bodu this, pokud by počátek byl v daném bodě basePoint.
		/// </summary>
		/// <param name="absolutePoint"></param>
		/// <param name="basePoint"></param>
		/// <returns></returns>
		public static PointF GetRelativePointFromBase(this PointF absolutePoint, PointF basePoint)
		{
			return new PointF(absolutePoint.X - basePoint.X, absolutePoint.Y - basePoint.Y);
		}
		/// <summary>
		/// Vrátí absolutní bod pro souřadnice bodu this, pokud by počátek byl v daném bodě basePoint.
		/// </summary>
		/// <param name="point"></param>
		/// <param name="relativePoint"></param>
		/// <returns></returns>
		public static PointF GetAbsolutePointFromBase(this PointF point, PointF relativePoint)
		{
			return new PointF(relativePoint.X + point.X, relativePoint.Y + point.Y);
		}
		/// <summary>
		/// Vrátí relativní bod pro souřadnice bodu this, pokud by počátek byl v daném bodě basePoint.
		/// </summary>
		/// <param name="absolutePoint"></param>
		/// <param name="basePoint"></param>
		/// <returns></returns>
		public static Point GetRelativePointFromBase(this PointF absolutePoint, Point basePoint)
		{
			return Point.Round(new PointF(absolutePoint.X - (float)basePoint.X, absolutePoint.Y - (float)basePoint.Y));
		}
		/// <summary>
		/// Vrátí absolutní bod pro souřadnice bodu this, pokud by počátek byl v daném bodě basePoint.
		/// </summary>
		/// <param name="point"></param>
		/// <param name="relativePoint"></param>
		/// <returns></returns>
		public static Point GetAbsolutePointFromBase(this PointF point, Point relativePoint)
		{
			return Point.Round(new PointF((float)relativePoint.X + point.X, (float)relativePoint.Y + point.Y));
		}
		#endregion
        #region Size, SizeF, Rectangle, RectangleF: zooming
        /// <summary>
        /// Zvětší danou velikost daným koeficientem.
        /// </summary>
        /// <param name="size">The SizeF structure to multiply.</param>
        /// <param name="zoom">Coefficient.</param>
        /// <returns>A SizeF structure that is the result of the multiply operation.</returns>
        public static SizeF Multiply(this SizeF size, decimal zoom)
        { return (new SizeD((decimal)size.Width * zoom, (decimal)size.Height * zoom)).ToSizeF(); }
        /// <summary>
        /// Zvětší danou velikost daným koeficientem.
        /// </summary>
        /// <param name="size">The SizeF structure to multiply.</param>
        /// <param name="zoom">Coefficient.</param>
        /// <returns>A SizeF structure that is the result of the multiply operation.</returns>
        public static SizeF Multiply(this SizeF size, float zoom)
        { return new SizeF(size.Width * zoom, size.Height * zoom); }
        /// <summary>
        /// Zvětší danou velikost daným koeficientem.
        /// </summary>
        /// <param name="size">The SizeF structure to multiply.</param>
        /// <param name="zoom">Coefficient.</param>
        /// <returns>A SizeF structure that is the result of the multiply operation.</returns>
        public static SizeF Multiply(this SizeF size, double zoom)
        { return new SizeF(size.Width * (float)zoom, size.Height * (float)zoom); }
        /// <summary>
        /// Zmenší danou velikost daným poměrem.
        /// </summary>
        /// <param name="size">The SizeF structure to divide.</param>
        /// <param name="ratio">Ratio.</param>
        /// <returns>A SizeF structure that is the result of the divide operation.</returns>
        public static SizeF Divide(this SizeF size, decimal ratio)
        { return (new SizeD((decimal)size.Width / ratio, (decimal)size.Height / ratio)).ToSizeF(); }
        /// <summary>
        /// Zmenší danou velikost daným poměrem.
        /// </summary>
        /// <param name="size">The SizeF structure to divide.</param>
        /// <param name="ratio">Ratio.</param>
        /// <returns>A SizeF structure that is the result of the divide operation.</returns>
        public static SizeF Divide(this SizeF size, float ratio)
        { return new SizeF(size.Width / ratio, size.Height / ratio); }
        /// <summary>
        /// Zmenší danou velikost daným poměrem.
        /// </summary>
        /// <param name="size">The SizeF structure to divide.</param>
        /// <param name="ratio">Ratio.</param>
        /// <returns>A SizeF structure that is the result of the divide operation.</returns>
        public static SizeF Divide(this SizeF size, double ratio)
        { return new SizeF(size.Width / (float)ratio, size.Height / (float)ratio); }

        #endregion
        #region RectangleF: FromPoints, FromDim, FromCenter
        /// <summary>
        /// Vrátí RectangleF, který je natažený mezi dvěma body, přičemž vzájemná pozice oněch dvou bodů může být libovolná.
        /// </summary>
        /// <param name="point1"></param>
        /// <param name="point2"></param>
        /// <returns></returns>
        public static RectangleF GetRectangleFFromPoints(this PointF point1, PointF point2)
        {
            float l = (point1.X < point2.X ? point1.X : point2.X);
            float t = (point1.Y < point2.Y ? point1.Y : point2.Y);
            float r = (point1.X > point2.X ? point1.X : point2.X);
            float b = (point1.Y > point2.Y ? point1.Y : point2.Y);
            return RectangleF.FromLTRB(l, t, r, b);
        }
		/// <summary>
		/// Vrátí RectangleF, který je nakreslený mezi souřadnicemi x1÷x2 a y1÷y2.
		/// </summary>
		/// <param name="x1"></param>
		/// <param name="x2"></param>
        /// <param name="y1"></param>
        /// <param name="y2"></param>
		/// <returns></returns>
		public static RectangleF GetRectangleFFromDim(float x1, float x2, float y1, float y2)
		{
			float l = (x1 < x2 ? x1 : x2);
			float t = (y1 < y2 ? y1 : y2);
			float r = (x1 > x2 ? x1 : x2);
			float b = (y1 > y2 ? y1 : y2);
			return RectangleF.FromLTRB(l, t, r, b);
		}
		/// <summary>
		/// Vrátí RectangleF, který je nakreslený mezi souřadnicemi x1÷x2 a y1÷y2.
		/// Pokud je vzdálenost mezi x1÷x2 nebo y1÷y2 menší než minDist, pak zachová vzdálenost minDist,
		/// a to tak, že v odpovídajícím směru upraví souřadnici x2 nebo y2. 
		/// Jako x2/y2 by tedy měla být zadána ta "pohyblivější".
		/// </summary>
		/// <returns></returns>
		public static RectangleF GetRectangleFFromDim(float x1, float x2, float y1, float y2, float minDist)
		{
			// Úprava souřadnic minDist (kladné číslo) a x2,y2:
			if (minDist < 0f) minDist = -minDist;
			if (x2 >= x1 && x2 - x1 < minDist)
				x2 = x1 + minDist;
			else if (x2 < x1 && x1 - x2 < minDist)
				x2 = x1 - minDist;
			if (y2 >= y1 && y2 - y1 < minDist)
				y2 = y1 + minDist;
			else if (y2 < y1 && y1 - y2 < minDist)
				y2 = y1 - minDist;

			float l = (x1 < x2 ? x1 : x2);
			float t = (y1 < y2 ? y1 : y2);
			float r = (x1 > x2 ? x1 : x2);
			float b = (y1 > y2 ? y1 : y2);
			return RectangleF.FromLTRB(l, t, r, b);
		}
        /// <summary>
        /// Vrátí bod uprostřed this RectangleF
        /// </summary>
        /// <param name="rectangleF"></param>
        /// <returns></returns>
        public static PointF Center(this RectangleF rectangleF)
        {
            return new PointF(rectangleF.X + rectangleF.Width / 2f, rectangleF.Y + rectangleF.Height / 2f);
        }
        /// <summary>
        /// Vrátí RectangleF postavený okolo středu this, v dané velikosti (size)
        /// </summary>
        /// <param name="point"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public static RectangleF GetRectangleFFromCenter(this PointF point, SizeF size)
        {
            PointF location = new PointF((point.X - size.Width / 2f), (point.Y - size.Height / 2f));
            return new RectangleF(location, size);
        }
        /// <summary>
        /// Vrátí RectangleF postavený okolo středu this, v dané velikosti (size)
        /// </summary>
        /// <param name="point"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        public static RectangleF GetRectangleFFromCenter(this PointF point, float width, float height)
        {
            PointF location = new PointF((point.X - width / 2f), (point.Y - height / 2f));
            return new RectangleF(location, new SizeF(width, height));
        }
        /// <summary>
        /// Vrátí RectangleF postavený okolo středu this, v dané velikosti (size)
        /// </summary>
        /// <param name="point"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public static RectangleF GetRectangleFFromCenter(this PointF point, float size)
        {
            PointF location = new PointF((point.X - size / 2f), (point.Y - size / 2f));
            return new RectangleF(location, new SizeF(size, size));
        }
        /// <summary>
        /// Vrátí RectangleF ve velikosti (Size) this, postavený okolo daného středu
        /// </summary>
        /// <param name="point"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public static RectangleF GetRectangleFFromCenter(this SizeF size, PointF point)
        {
            PointF location = new PointF((point.X - size.Width / 2f), (point.Y - size.Height / 2f));
            return new RectangleF(location, size);
        }
        #endregion
        #region RectangleF: RelativePoint, AbsolutePoint
        /// <summary>
        /// Vrátí relativní pozici daného absolutního bodu (absolutePoint) vzhledem k this (RectangleF).
        /// Relativní pozice je v rozmezí 0 (na souřadnici Left nebo Top) až 1 (na souřadnici Right nebo Bottom).
        /// Relativní pozice může být menší než 0 (vlevo nebo nad this), nebo větší než 1 (vpravo nebo pod this).
        /// Tedy hodnoty 0 a 1 jsou na hraně this, hodnoty mezi 0 a 1 jsou uvnitř this, a hodnoty mimo jsou mimo this.
        /// </summary>
        /// <param name="rectangleF"></param>
        /// <param name="absolutePoint"></param>
        /// <returns></returns>
        public static PointF GetPointFRelative(this RectangleF rectangleF, PointF absolutePoint)
        {
            return new PointF(
                (float)_GetRelative(rectangleF.X, rectangleF.Right, absolutePoint.X),
                (float)_GetRelative(rectangleF.Y, rectangleF.Bottom, absolutePoint.Y));
        }
        /// <summary>
        /// Vrátí relativní pozici daného absolutního bodu (absolutePoint) vzhledem k this (RectangleF).
        /// Relativní pozice je v rozmezí 0 (na souřadnici Left nebo Top) až 1 (na souřadnici Right nebo Bottom).
        /// Relativní pozice může být menší než 0 (vlevo nebo nad this), nebo větší než 1 (vpravo nebo pod this).
        /// Tedy hodnoty 0 a 1 jsou na hraně this, hodnoty mezi 0 a 1 jsou uvnitř this, a hodnoty mimo jsou mimo this.
        /// </summary>
        /// <param name="rectangleF"></param>
        /// <param name="absolutePoint"></param>
        /// <returns></returns>
        public static PointD GetPointDRelative(this RectangleF rectangleF, PointF absolutePoint)
        {
            return new PointD(
                _GetRelative(rectangleF.X, rectangleF.Right, absolutePoint.X),
                _GetRelative(rectangleF.Y, rectangleF.Bottom, absolutePoint.Y));
        }
        /// <summary>
        /// Vrátí relativní pozici daného absolutního bodu 
        /// vzhledem k bodům begin (relativní pozice = 0) a end (relativní pozice = 1).
        /// Pokud mezi body begin a end je vzdálenost 0, pak vrací 0.
        /// </summary>
        /// <param name="begin"></param>
        /// <param name="end"></param>
        /// <param name="absolute"></param>
        /// <returns></returns>
        private static decimal _GetRelative(float begin, float end, float absolute)
        {
            decimal offset = (decimal)(absolute - begin);
            decimal size = (decimal)(end - begin);
            if (size == 0m) return 0m;
            return offset / size;
        }
        /// <summary>
        /// Vrátí souřadnice bodu, který v this rectangle odpovídá dané relativní souřadnici.
        /// Relativní souřadnice vyjadřuje pozici bodu: hodnota 0=na pozici Left nebo Top, hodnota 1=na pozici Right nebo Bottom.
        /// Vrácený bod je vyjádřen v reálných (absolutních) hodnotách odpovídajících rectanglu this.
        /// </summary>
        /// <param name="rectangleF"></param>
        /// <param name="relativePoint"></param>
        /// <returns></returns>
        public static PointF GetPointFAbsolute(this RectangleF rectangleF, PointF relativePoint)
        {
            return new PointF(
                (float)_GetAbsolute(rectangleF.X, rectangleF.Right, (decimal)relativePoint.X),
                (float)_GetAbsolute(rectangleF.Y, rectangleF.Bottom, (decimal)relativePoint.Y));
        }
        /// <summary>
        /// Vrátí souřadnice bodu, který v this rectangle odpovídá dané relativní souřadnici.
        /// Relativní souřadnice vyjadřuje pozici bodu: hodnota 0=na pozici Left nebo Top, hodnota 1=na pozici Right nebo Bottom.
        /// Vrácený bod je vyjádřen v reálných (absolutních) hodnotách odpovídajících rectanglu this.
        /// </summary>
        /// <param name="rectangleF"></param>
        /// <param name="relativePoint"></param>
        /// <returns></returns>
        public static PointF GetPointFAbsolute(this RectangleF rectangleF, PointD relativePoint)
        {
            return new PointF(
                (float)_GetAbsolute(rectangleF.X, rectangleF.Right, relativePoint.X),
                (float)_GetAbsolute(rectangleF.Y, rectangleF.Bottom, relativePoint.Y));
        }
        /// <summary>
        /// Vrátí absolutní pozici daného relativního bodu 
        /// vzhledem k bodům begin (relativní pozice = 0) a end (relativní pozice = 1).
        /// Pokud mezi body begin a end je vzdálenost 0, pak vrací begin.
        /// </summary>
        /// <param name="begin"></param>
        /// <param name="end"></param>
        /// <param name="relative"></param>
        /// <returns></returns>
        private static float _GetAbsolute(float begin, float end, decimal relative)
        {
            decimal size = (decimal)(end - begin);
            if (size == 0m) return begin;
            return begin + (float)(relative * size);
        }
        /// <summary>
        /// Vrátí souřadnice prostoru (RectangleF), vytvořené z bodu (this) a dané velikosti,
        /// tak, že bod this bude ve výsledném prostoru ležet na dané relativní pozici
        /// </summary>
        /// <param name="fixedPoint"></param>
        /// <param name="size"></param>
        /// <param name="relativePivotPoint"></param>
        /// <returns></returns>
        public static RectangleF GetRectangleFFromRelativePoint(this PointF fixedPoint, SizeF size, PointD relativePivotPoint)
        {
            float x = _GetBeginFromRelative(fixedPoint.X, size.Width, relativePivotPoint.X);
            float y = _GetBeginFromRelative(fixedPoint.Y, size.Height, relativePivotPoint.Y);
            return new RectangleF(new PointF(x, y), size);
        }

        private static float _GetBeginFromRelative(float fix, float size, decimal relative)
        {
            return fix - (float)((decimal)size * relative);
        }
        #endregion
        #region RectangleF: MoveEdge, MovePoint
        /// <summary>
        /// Vrátí RectangleF, který vytvoří z this RectangleF, když jeho hranu (edge) posune na novou souřadnici
        /// </summary>
        /// <param name="rectangleF"></param>
        /// <param name="edge"></param>
        /// <param name="dimension"></param>
        /// <returns></returns>
        public static RectangleF MoveEdge(this RectangleF rectangleF, RectangleEdge edge, float dimension)
        {
            float x1 = rectangleF.X;
            float x2 = rectangleF.Right;
            float y1 = rectangleF.Y;
            float y2 = rectangleF.Bottom;
            switch (edge)
            {
                case RectangleEdge.Top:
                    return GetRectangleFFromDim(x1, x2, dimension, y2);
                case RectangleEdge.Right:
                    return GetRectangleFFromDim(x1, dimension, y1, y2);
                case RectangleEdge.Bottom:
                    return GetRectangleFFromDim(x1, x2, y1, dimension);
                case RectangleEdge.Left:
                    return GetRectangleFFromDim(dimension, x2, y1, y2);
            }
            return rectangleF;
        }
        /// <summary>
        /// Vrátí PointF, který leží na daném rohu this RectangleF
        /// </summary>
        /// <param name="rectangleF"></param>
        /// <param name="corner"></param>
        /// <returns></returns>
        public static PointF GetPoint(this RectangleF rectangleF, RectangleCorner corner)
        {
            float x1 = rectangleF.X;
            float x2 = rectangleF.Right;
            float y1 = rectangleF.Y;
            float y2 = rectangleF.Bottom;
            switch (corner)
            {
                case RectangleCorner.LeftTop:
                    return new PointF(x1, y1);
                case RectangleCorner.TopRight:
                    return new PointF(x2, y1);
                case RectangleCorner.RightBottom:
                    return new PointF(x2, y2);
                case RectangleCorner.BottomLeft:
                    return new PointF(x1, y2);
            }
            return PointF.Empty;
        }
        /// <summary>
        /// Vrátí RectangleF, který vytvoří z this RectangleF, když jeho bod (corner) posune na nové souřadnice
        /// </summary>
        /// <param name="rectangleF"></param>
        /// <param name="corner"></param>
        /// <param name="point"></param>
        /// <returns></returns>
        public static RectangleF MovePoint(this RectangleF rectangleF, RectangleCorner corner, PointF point)
        {
            switch (corner)
            {
                case RectangleCorner.LeftTop:
                    return GetRectangleFFromPoints(rectangleF.GetPoint(RectangleCorner.RightBottom), point);
                case RectangleCorner.TopRight:
                    return GetRectangleFFromPoints(rectangleF.GetPoint(RectangleCorner.BottomLeft), point);
                case RectangleCorner.RightBottom:
                    return GetRectangleFFromPoints(rectangleF.GetPoint(RectangleCorner.LeftTop), point);
                case RectangleCorner.BottomLeft:
                    return GetRectangleFFromPoints(rectangleF.GetPoint(RectangleCorner.TopRight), point);
            }
            return rectangleF;
        }
        #endregion
        #region Rectangle, RectangleF: GetArea(), SummaryRectangle()
        /// <summary>
        /// Vrací plochu daného Rectangle
        /// </summary>
        /// <param name="r"></param>
        /// <returns></returns>
        public static int GetArea(this Rectangle r)
        {
            return r.Width * r.Height;
        }
        /// <summary>
        /// Vrací plochu daného Rectangle
        /// </summary>
        /// <param name="r"></param>
        /// <returns></returns>
        public static float GetArea(this RectangleF r)
        {
            return r.Width * r.Height;
        }
        /// <summary>
        /// Vrací RectangleF, který je souhrnem všech zadaných Rectangle.
        /// </summary>
        /// <param name="items"></param>
        /// <returns></returns>
        public static RectangleF SummaryRectangle(IEnumerable<RectangleF> items)
        {
            float l = 0f;
            float t = 0f;
            float r = 0f;
            float b = 0f;
            bool empty = true;
            foreach (RectangleF item in items)
            {
                if (empty)
                {
                    l = item.Left;
                    t = item.Top;
                    r = item.Right;
                    b = item.Bottom;
                    empty = false;
                }
                else
                {
                    if (l > item.Left) l = item.Left;
                    if (t > item.Top) t = item.Top;
                    if (r < item.Right) r = item.Right;
                    if (b < item.Bottom) b = item.Bottom;
                }
            }
            return RectangleF.FromLTRB(l, t, r, b);
        }
        #endregion
        #region Type
        /// <summary>
        /// Vrátí Namespace.Name
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string NsName(this Type type)
        {
            return type.Namespace + "." + type.Name;
        }
        #endregion
    }
    #region enum RectangleEdge, RectangleCorner
    /// <summary>
    /// Vyjádření názvu hrany na objektu Rectangle (Horní, Vpravo, Dolní, Vlevo)
    /// </summary>
    public enum RectangleEdge
    {
        /// <summary>Neurčeno</summary>
        None = 0,
        /// <summary>Horní</summary>
        Top,
        /// <summary>Vpravo</summary>
        Right,
        /// <summary>Dolní</summary>
        Bottom,
        /// <summary>Vlevo</summary>
        Left
    }
    /// <summary>
    /// Vyjádření názvu rohu na objektu Rectangle (Vlevo nahoře, Vpravo nahoře, ...)
    /// </summary>
    public enum RectangleCorner
    {
        /// <summary>Neurčeno</summary>
        None = 0,
        /// <summary>Vlevo nahoře</summary>
        LeftTop,
        /// <summary>Vpravo nahoře</summary>
        TopRight,
        /// <summary>Vpravo dole</summary>
        RightBottom,
        /// <summary>Vlevo dole</summary>
        BottomLeft
    }
    #endregion
    #region class SysCursors, enum SysCursorType
    /// <summary>
	/// Práce s objekty Cursor
	/// </summary>
	internal class SysCursors
	{
		public static Cursor GetCursor(SysCursorType cursorType)
		{
			switch (cursorType)
			{
				case SysCursorType.AppStarting: return Cursors.AppStarting;
				case SysCursorType.Arrow: return Cursors.Arrow;
				case SysCursorType.Cross: return Cursors.Cross;
				case SysCursorType.Default: return Cursors.Default;
				case SysCursorType.Hand: return Cursors.Hand;
				case SysCursorType.Help: return Cursors.Help;
				case SysCursorType.HSplit: return Cursors.HSplit;
				case SysCursorType.IBeam: return Cursors.IBeam;
				case SysCursorType.No: return Cursors.No;
				case SysCursorType.NoMove2D: return Cursors.NoMove2D;
				case SysCursorType.NoMoveHoriz: return Cursors.NoMoveHoriz;
				case SysCursorType.NoMoveVert: return Cursors.NoMoveVert;
				case SysCursorType.PanEast: return Cursors.PanEast;
				case SysCursorType.PanNE: return Cursors.PanNE;
				case SysCursorType.PanNorth: return Cursors.PanNorth;
				case SysCursorType.PanNW: return Cursors.PanNW;
				case SysCursorType.PanSE: return Cursors.PanSE;
				case SysCursorType.PanSouth: return Cursors.PanSouth;
				case SysCursorType.PanSW: return Cursors.PanSW;
				case SysCursorType.PanWest: return Cursors.PanWest;
				case SysCursorType.SizeAll: return Cursors.SizeAll;
				case SysCursorType.SizeNESW: return Cursors.SizeNESW;
				case SysCursorType.SizeNS: return Cursors.SizeNS;
				case SysCursorType.SizeNWSE: return Cursors.SizeNWSE;
				case SysCursorType.SizeWE: return Cursors.SizeWE;
				case SysCursorType.UpArrow: return Cursors.UpArrow;
				case SysCursorType.VSplit: return Cursors.VSplit;
				case SysCursorType.WaitCursor: return Cursors.WaitCursor;

                case SysCursorType.ExtCrossDoc: return Pics.IconLibraryMin.Cross_r_doc;
			}
			return Cursors.Default;
		}
	}
    /// <summary>
    /// Druh kurzoru.
    /// Větší část hodnot odpovídá systémovým kurzorům třídy System.Windows.Forms.Cursors
    /// Konkrétní objekt Cursor lze získat statickou metodou třídy SysCursors.GetCursor(SysCursorType cursorType)
    /// </summary>
	public enum SysCursorType
	{
		/// <summary>No change (žádná změna)</summary>
		Null = 0,
		/// <summary>cursor that appears when an application starts.</summary>
		AppStarting,
		/// <summary>arrow cursor.</summary>
		Arrow,
		/// <summary>crosshair cursor.</summary>
		Cross,
		/// <summary>default cursor, which is usually an arrow cursor.</summary>
		Default,
		/// <summary>hand cursor, typically used when hovering over a Web link.</summary>
		Hand,
		/// <summary>Help cursor, which is a combination of an arrow and a question mark.</summary>
		Help,
		/// <summary>cursor that appears when the mouse is positioned over a horizontal splitter bar.</summary>
		HSplit,
		/// <summary>I-beam cursor, which is used to show where the text cursor appears when the mouse is clicked.</summary>
		IBeam,
		/// <summary>cursor that indicates that a particular region is invalid for the current operation.</summary>
		No,
		/// <summary>cursor that appears during wheel operations when the mouse is not moving, but the window can be scrolled in both a horizontal and vertical direction.</summary>
		NoMove2D,
		/// <summary>cursor that appears during wheel operations when the mouse is not moving, but the window can be scrolled in a horizontal direction.</summary>
		NoMoveHoriz,
		/// <summary>cursor that appears during wheel operations when the mouse is not moving, but the window can be scrolled in a vertical direction.</summary>
		NoMoveVert,
		/// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling horizontally to the right.</summary>
		PanEast,
		/// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling horizontally and vertically upward and to the right.</summary>
		PanNE,
		/// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling vertically in an upward direction.</summary>
		PanNorth,
		/// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling horizontally and vertically upward and to the left.</summary>
		PanNW,
		/// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling horizontally and vertically downward and to the right.</summary>
		PanSE,
		/// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling vertically in a downward direction.</summary>
		PanSouth,
		/// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling horizontally and vertically downward and to the left.</summary>
		PanSW,
		/// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling horizontally to the left.</summary>
		PanWest,
		/// <summary>four-headed sizing cursor, which consists of four joined arrows that point north, south, east, and west.</summary>
		SizeAll,
		/// <summary>two-headed diagonal (northeast/southwest) sizing cursor.</summary>
		SizeNESW,
		/// <summary>two-headed vertical (north/south) sizing cursor.</summary>
		SizeNS,
		/// <summary>two-headed diagonal (northwest/southeast) sizing cursor.</summary>
		SizeNWSE,
		/// <summary>two-headed horizontal (west/east) sizing cursor.</summary>
		SizeWE,
		/// <summary>up arrow cursor, typically used to identify an insertion point.</summary>
		UpArrow,
		/// <summary>cursor that appears when the mouse is positioned over a vertical splitter bar.</summary>
		VSplit,
		/// <summary>wait cursor, typically an hourglass shape.</summary>
		WaitCursor,
		/// <summary>Ne-systémový kurzor: kříž s obdélníkem vpravo dole, pro insert objektu.</summary>
        ExtCrossDoc,
		/// <summary>
		/// Nejde o standardní kurzor, ale o požadavek na kurzor typu NoMoveHoriz nebo NoMoveVert nebo NoMove2D, podle aktuálního typu přesunu.
		/// </summary>
		NoMoveAuto
	}
	#endregion
}
